import { BaseMgr } from "../base/BaseMgr";
import WSClient, { IWSClient } from "../net/WSClient";
import { IProtocol, IProtocolMsg } from "../protocol/IProtocol";
import ProtocolPool from "../protocol/ProtocolPool";

/**
 * 网络模块
 */
export default class NetMgr extends BaseMgr {
    public static get inst() {
        return this.getInstance();
    }
    /** websocket */
    ws: WSClient = null!;
    /** protocolpool */
    protocolPool: ProtocolPool = null!;

    pendingRequests: { [requestId: string]: { resolve: (val) => void, reject: () => void } } = {};

    constructor() {
        super();
        this.ws = new WSClient();
        this.protocolPool = new ProtocolPool();
    }

    param: IWSClient = null!;
    connectWs(param: IWSClient) {
        if (this.ws.isConnect) return;
        this.param = param;
        this.ws.connect({
            url: param.url,
            onOpen: this.onOpen.bind(this),
            onError: this.onError.bind(this),
            onMessage: this.onMessage.bind(this),
            onClose: this.onClose.bind(this),
        });
    }

    closeWs() {
        if (this.ws)
            this.ws.close()
    }

    registerProtocol<T>(msgId: number, protocol: IProtocol, onMsg?: IProtocolMsg<T>) {
        this.protocolPool.register(msgId, protocol, onMsg);
    }

    send<T>(msgId: number, encodeProto: IProtocol<T>, data: T) {
        console.log("send", msgId, data)
        const body = encodeProto.encode(data).finish()
        this.sendMsg(msgId, body);
    }

    sendMsg<T>(msgId: number, data: Uint8Array, decodeProtocol?: { prototype: T }, cb?: (val) => void): Promise<T> {
        return new Promise((resolve, reject) => {
            if (!this.ws || this.ws.conn.readyState != this.ws.conn.OPEN) {
                console.error("send failed, ws is not connet", msgId);
                return false;
            }
            if (!cb) {
                cb = resolve
            }
            if (cb && decodeProtocol)
                this.protocolPool.registerDecodeProtocol(msgId, decodeProtocol as any, cb);

            const buffer = new ArrayBuffer(data.length + 8);
            const dataView = new DataView(buffer);
            dataView.setInt32(0, data.length + 8);
            dataView.setInt32(4, msgId);
            if (data.length > 0) {
                const unit8buf = new Uint8Array(buffer);
                unit8buf.set(data, 8);
            }
            this.ws.send(msgId, buffer);
        });
    }

    onOpen(e: Event) {
        this.param?.onOpen && this.param?.onOpen(e);
    }

    onError(e: Event) {
        this.param?.onError && this.param?.onError(e);
    }

    onMessage(e: MessageEvent) {
        if (e.data) {
            console.log("on msg:", e.data)
            let dataView = new DataView(e.data);
            const unit8buf = new Uint8Array(e.data);
            const dataLen = dataView.getInt32(0)
            const msgId = dataView.getInt32(4)
            const body = unit8buf.subarray(8);
            this.protocolPool.recive(msgId, body);
        }
        this.param?.onMessage && this.param?.onMessage(e);
    }

    onClose(e: CloseEvent) {
        this.param?.onClose && this.param?.onClose(e);
    }
}